package ar.edu.uba.fi.jmit.receiver;

import java.io.BufferedReader;
import java.io.IOException;

import javax.servlet.ServletException;

import ar.edu.uba.fi.jmit.integrationcommons.Message;
import ar.edu.uba.fi.jmit.integrationcommons.MessagePriority;
import ar.edu.uba.fi.jmit.receiver.exception.ReceiverException;
import ar.edu.uba.fi.jmit.receiver.mock.HttpServletRequestMock;
import ar.edu.uba.fi.jmit.receiver.mock.HttpServletResponseMock;
import ar.edu.uba.fi.jmit.receiver.mock.ReceiverFactoryMock;

public class PollingConsumerHttpImplTestCase extends ReceiverTestCase {
	
	@Override
	protected void setUp() throws Exception {
		ReceiverFactoryMock.useMock();
	}

	
	public void testPost() {
		try {
			PollingConsumerHttpImpl consumer = new PollingConsumerHttpImpl();
			consumer.init();
			assertEquals(0, consumer.getActualMessageCount());
			assertEquals(0L, consumer.getTotalMessages());
			assertEquals(0L, consumer.getTotalRequests());
			consumer.service(this.createServletRequest(1), this.createServletResponse());
			assertEquals(1, consumer.getActualMessageCount());
			assertEquals(1L, consumer.getTotalMessages());
			assertEquals(1L, consumer.getTotalRequests());
			Message message = consumer.take();
			assertEquals("1", message.getHeader().getId());
			assertEquals(0, consumer.getActualMessageCount());
			assertEquals(1L, consumer.getTotalMessages());
			assertEquals(1L, consumer.getTotalRequests());
		} catch (Exception e) {
			fail(e);
		}
	}
	
	public void testInvalidRequest() {
		PollingConsumerHttpImpl consumer = null;
		try {
			consumer = new PollingConsumerHttpImpl();
			consumer.init();
			assertEquals(0, consumer.getActualMessageCount());
			assertEquals(0L, consumer.getTotalMessages());
			assertEquals(0L, consumer.getTotalRequests());
			HttpServletRequestMock request = new HttpServletRequestMock();
			request.setMethod("POST");
			consumer.service(request, this.createServletResponse());
			fail("Exception should be thrown");
		} catch (ServletException e) {
			//expected
		} catch (IOException e) {
			fail(e);
		}
		assertEquals(0, consumer.getActualMessageCount());
		assertEquals(0L, consumer.getTotalMessages());
		assertEquals(1L, consumer.getTotalRequests());
	}
	
	public void testPostMany() {
		try {
			PollingConsumerHttpImpl consumer = new PollingConsumerHttpImpl();
			consumer.init();
			for(int i = 0; i < 100; i++) {
				assertEquals(i, consumer.getActualMessageCount());
				assertEquals((long)i, consumer.getTotalMessages());
				assertEquals((long)i, consumer.getTotalRequests());
				consumer.service(this.createServletRequest(i), this.createServletResponse());
			}
			
			for(int i = 0; i < 100; i++) {
				Message message = consumer.take();
				assertEquals(String.valueOf(i), message.getHeader().getId());
				assertEquals(99 - i, consumer.getActualMessageCount());
				assertEquals(100L, consumer.getTotalMessages());
				assertEquals(100L, consumer.getTotalRequests());
			}
		} catch (Exception e) {
			fail(e);
		}
	}
	
	public void testPriority() {
		PollingConsumerHttpImpl consumer = new PollingConsumerHttpImpl();
		try {
			consumer.init();
			consumer.service(this.createServletRequest(1, MessagePriority.NORMAL), this.createServletResponse());
			consumer.service(this.createServletRequest(2, MessagePriority.HIGH_PRIORITY), this.createServletResponse());
			assertEquals("The second added message should be the first to come out becouse of priority", 
					"2", consumer.take().getHeader().getId());
			assertEquals("1", consumer.take().getHeader().getId());
		} catch (Exception e) {
			fail(e);
		}
	}
	
	public void testBlockingTake() {
		PollingConsumerHttpImpl consumer = new PollingConsumerHttpImpl();
		TakerThread takerThread = new TakerThread(consumer);
		
		try {
			consumer.init();
			assertEquals(0, consumer.getActualMessageCount());
			takerThread.start();
			Thread.sleep(200);
			assertFalse(takerThread.isMessageTaken());
			consumer.service(this.createServletRequest(1), this.createServletResponse());
			Thread.sleep(200);
			assertEquals(0, consumer.getActualMessageCount());
			assertTrue(takerThread.isMessageTaken());
		} catch (Exception e) {
			fail(e);
		}
	}
	
	public void testPostNoMessage() {
		try {
			PollingConsumerHttpImpl consumer = new PollingConsumerHttpImpl();
			consumer.init();
			assertEquals(0, consumer.getActualMessageCount());
			assertEquals(0L, consumer.getTotalMessages());
			assertEquals(0L, consumer.getTotalRequests());
			HttpServletRequestMock request = new HttpServletRequestMock();
			request.setMethod("POST");
			try {
				consumer.service(request, this.createServletResponse());
				fail("Exception should be thrown");
			}catch(ServletException e) {
				assertEquals(ReceiverException.class, e.getRootCause().getClass());
				assertEquals("There was an error reading request. Could not buld Message.", e.getMessage());
				assertEquals(0L, consumer.getTotalMessages());
				assertEquals(1L, consumer.getTotalRequests());
			}
		} catch (Exception e) {
			fail(e);
		}
	}
	
	public void testPostInvalidObject() {
		try {
			PollingConsumerHttpImpl consumer = new PollingConsumerHttpImpl();
			consumer.init();
			assertEquals(0, consumer.getActualMessageCount());
			assertEquals(0L, consumer.getTotalMessages());
			assertEquals(0L, consumer.getTotalRequests());
			HttpServletRequestMock request = new HttpServletRequestMock("Something");
			request.setMethod("POST");
			try {
				consumer.service(request, this.createServletResponse());
				fail("Exception should be thrown");
			}catch(ServletException e) {
				assertEquals(ReceiverException.class, e.getRootCause().getClass());
				assertEquals("The recived request is not a Message", e.getMessage());
				assertEquals(0L, consumer.getTotalMessages());
				assertEquals(1L, consumer.getTotalRequests());
			}
		} catch (Exception e) {
			fail(e);
		}
	}
	
	public void testGet() {
		try {
			PollingConsumerHttpImpl consumer = new PollingConsumerHttpImpl();
			consumer.init();
			for(int i = 0; i < 10; i++) {
				consumer.service(this.createServletRequest(i), this.createServletResponse());
			}
			try {
				HttpServletRequestMock request = new HttpServletRequestMock();
				request.setMethod("POST");
				consumer.service(request, this.createServletResponse());
			} catch (ServletException e) {
				//expected
			}
			HttpServletRequestMock request = new HttpServletRequestMock();
			request.setMethod("GET");
			HttpServletResponseMock response = this.createServletResponse();
			consumer.service(request, response);
			BufferedReader reader = response.getReader();
			reader.ready();
			String line = reader.readLine();
			assertEquals("<h1>Receiver Statistics</h1>", line);
			line = reader.readLine();
			assertEquals("Total recived Messages: 10</br>", line);
			line = reader.readLine();
			assertEquals("Total recived Requests: 11</br>", line);
			line = reader.readLine();
			assertEquals("Actual Message Count: 10</br>", line);
		} catch (Exception e) {
			fail(e);
		}
	}
	
	private class TakerThread extends Thread {
		
		private PollingConsumer consumer;
		
		private boolean messageTaken = false;
		
		public TakerThread(PollingConsumer consumer) {
			this.consumer = consumer;
		}
		
		public void run() {
			try {
				consumer.take();
				messageTaken = true;
			} catch (InterruptedException e) {
				fail(e);
			}
		}

		public boolean isMessageTaken() {
			return messageTaken;
		}
	}

}
